﻿using System;
using System.Diagnostics;
using System.Windows.Forms;
using Deferred.Editor.Content;
using Deferred.Library;
using Deferred.Library.Deferred;
using Deferred.Library.Input;
using Deferred.Library.Scene;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Keys = Microsoft.Xna.Framework.Input.Keys;

namespace Deferred.Editor.Gui
{
    public class RenderControl : GraphicsDeviceControl
    {
        private bool initialized = false;
        private ContentUpdater sceneUpdater;
        private KeyboardState currentKeyboardState;
        private GameTime gameTime;
        private Stopwatch elapsedTime = new Stopwatch();
        private Stopwatch totalTime = new Stopwatch();
        private DeferredRenderEngine renderEngine;
        private KeyboardHandler keyboardHandler;
        private IFocusHelper focusHelper;
        private DeferredBase deferredBase;
        private SceneGraph scene;
        private FpsCounter fpsCounter;

        public RenderControl(DeferredBase deferredBase, ContentUpdater sceneUpdater)
            :base(deferredBase.DeviceService)
        {
            this.deferredBase = deferredBase;
            this.sceneUpdater = sceneUpdater;
            sceneUpdater.CurrentSceneChanged += new EventHandler(delegate(object s, EventArgs e) { updateScene(); });

            fpsCounter = new FpsCounter(deferredBase);

            keyboardHandler = new KeyboardHandler(deferredBase.Services);
            deferredBase.Services.AddService(typeof(IKeyboardHandler), keyboardHandler);

            QuadRenderer quadRenderer = new QuadRenderer(deferredBase.DeviceService);
            deferredBase.Services.AddService(typeof(IQuadRenderer), quadRenderer);

            totalTime.Start();

            EventHandler resetDeviceHandler = new EventHandler(delegate(object s, EventArgs e){ResetDevice();});
            this.SizeChanged += resetDeviceHandler;
            this.VisibleChanged += resetDeviceHandler;
            this.MouseDown += new MouseEventHandler(RenderControl_MouseDown);
        }

        private void updateScene()
        {
            scene = sceneUpdater.CurrentScene;
            if (scene == null)
            {
                renderEngine = null;
            }
            else
            {
                renderEngine = new DeferredRenderEngine(deferredBase, scene);
                if (initialized) renderEngine.Initialize();
            }
        }

        private void ResetDevice()
        {
            Cursor originalCursor = Cursor;

            Cursor = Cursors.WaitCursor;
            ((GraphicsDeviceService)deferredBase.DeviceService).ResetDevice(Handle, Bounds.Width, Bounds.Height);
            Cursor = originalCursor;
        }

        protected override void Dispose(bool disposing)
        {
            deferredBase.Services.RemoveService(typeof(IKeyboardHandler));
            deferredBase.Services.RemoveService(typeof(IQuadRenderer));

            base.Dispose(disposing);
        }

        void RenderControl_MouseDown(object sender, MouseEventArgs e)
        {
            this.Select();
        }

        protected override void Initialize()
        {
            if (renderEngine != null) renderEngine.Initialize();
            keyboardHandler.Initialize();
            fpsCounter.Initialize();

            focusHelper = (IFocusHelper)deferredBase.Services.GetService(typeof(IFocusHelper));

            Application.Idle += delegate { Invalidate(); };

            initialized = true;


        }

        void timer_Tick(object sender, System.EventArgs e)
        {
            this.Invalidate();

            this.Update();
        }

        protected override void Draw()
        {
            UpdateGameTime();
            keyboardHandler.Update(gameTime);
            fpsCounter.Update(gameTime);
            updateCamera(gameTime);

            UpdateGameTime();
            if (renderEngine != null) renderEngine.Draw(gameTime);
            fpsCounter.Draw(gameTime);
        }

        void UpdateGameTime()
        {
            gameTime = new GameTime(totalTime.Elapsed, elapsedTime.Elapsed, totalTime.Elapsed, elapsedTime.Elapsed);

            elapsedTime.Reset();
            elapsedTime.Start();
        }

        private void updateCamera(GameTime gameTime)
        {
            if ((null == focusHelper || focusHelper.GameHasFocus) && scene!= null && scene.Camera != null)
            {
                currentKeyboardState = Keyboard.GetState();

                float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;

                // Check for input to rotate the camera up and down around the model.
                if (currentKeyboardState.IsKeyDown(Keys.Up) ||
                    currentKeyboardState.IsKeyDown(Keys.W))
                {
                    scene.Camera.CameraArc += time * 0.1f;
                }

                if (currentKeyboardState.IsKeyDown(Keys.Down) ||
                    currentKeyboardState.IsKeyDown(Keys.S))
                {
                    scene.Camera.CameraArc -= time * 0.1f;
                }

                // Check for input to rotate the camera around the model.
                if (currentKeyboardState.IsKeyDown(Keys.Right) ||
                    currentKeyboardState.IsKeyDown(Keys.D))
                {
                    scene.Camera.CameraRotation += time * 0.1f;
                }

                if (currentKeyboardState.IsKeyDown(Keys.Left) ||
                    currentKeyboardState.IsKeyDown(Keys.A))
                {
                    scene.Camera.CameraRotation -= time * 0.1f;
                }

                // Check for input to zoom camera in and out.
                if (currentKeyboardState.IsKeyDown(Keys.Z))
                    scene.Camera.CameraDistance += time * 0.25f;

                if (currentKeyboardState.IsKeyDown(Keys.X))
                    scene.Camera.CameraDistance -= time * 0.25f;

                if (currentKeyboardState.IsKeyDown(Keys.R))
                {
                    scene.Camera.CameraArc = -30;
                    scene.Camera.CameraRotation = 0;
                    scene.Camera.CameraDistance = 100;
                }
            }
        }
    }

}
